/*-------------------------------------------------------------------------
 *
 * dropcmds.c
 *	  处理各种 "DROP" 操作
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/commands/dropcmds.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/namespace.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_class.h"
#include "catalog/pg_proc.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"


static void fc_does_not_exist_skipping(ObjectType fc_objtype,
									Node *fc_object);
static bool fc_owningrel_does_not_exist_skipping(List *fc_object,
											  const char **fc_msg, char **fc_name);
static bool fc_schema_does_not_exist_skipping(List *fc_object,
										   const char **fc_msg, char **fc_name);
static bool fc_type_in_list_does_not_exist_skipping(List *fc_typenames,
												 const char **fc_msg, char **fc_name);


/*
 * 删除一个或多个对象。
 *
 * 我们目前并不处理所有对象类型。例如，关系需要特殊处理，因为（例如）索引有额外的锁定要求。
 *
 * 我们首先查找所有对象，然后在一次 performMultipleDeletions() 调用中删除它们。这避免了如果它们之间存在依赖关系而产生不必要的 DROP RESTRICT 错误。
 */
void RemoveObjects(DropStmt *fc_stmt)
{
	ObjectAddresses *fc_objects;
	ListCell   *fc_cell1;

	fc_objects = new_object_addresses();

	foreach(fc_cell1, fc_stmt->objects)
	{
		ObjectAddress fc_address;
		Node	   *fc_object = lfirst(fc_cell1);
		Relation	fc_relation = NULL;
		Oid			fc_namespaceId;

		/* 获取对象的 ObjectAddress。 */
		fc_address = get_object_address(fc_stmt->removeType,
									 fc_object,
									 &fc_relation,
									 AccessExclusiveLock,
									 fc_stmt->missing_ok);

		/*
		 * 如果提供的对象未找到，则发布 NOTICE。请注意，这只在 missing_ok 的情况下相关，因为否则
		 * get_object_address 会抛出错误。
		 */
		if (!OidIsValid(fc_address.objectId))
		{
			Assert(fc_stmt->missing_ok);
			fc_does_not_exist_skipping(fc_stmt->removeType, fc_object);
			continue;
		}

		/*
		 * 尽管 COMMENT ON FUNCTION、SECURITY LABEL ON FUNCTION 等
		 * 可以像对待其他任何函数一样处理聚合函数，但在历史上，
		 * 我们不允许对 DROP FUNCTION 这样做。
		 */
		if (fc_stmt->removeType == OBJECT_FUNCTION)
		{
			if (get_func_prokind(fc_address.objectId) == PROKIND_AGGREGATE)
				ereport(ERROR,
						(errcode(ERRCODE_WRONG_OBJECT_TYPE),
						 errmsg("\"%s\" is an aggregate function",
								NameListToString(castNode(ObjectWithArgs, fc_object)->objname)),
						 errhint("Use DROP AGGREGATE to drop aggregate functions.")));
		}

		/* 检查权限。 */
		fc_namespaceId = get_object_namespace(&fc_address);
		if (!OidIsValid(fc_namespaceId) ||
			!pg_namespace_ownercheck(fc_namespaceId, GetUserId()))
			check_object_ownership(GetUserId(), fc_stmt->removeType, fc_address,
								   fc_object, fc_relation);

		/*
		 * 如果在此交易中访问了临时命名空间，请记下。
		 */
		if (OidIsValid(fc_namespaceId) && isTempNamespace(fc_namespaceId))
			MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;

		/* 释放任何 relcache 引用计数，但在提交之前保持锁定。 */
		if (fc_relation)
			table_close(fc_relation, NoLock);

		add_exact_object_address(&fc_address, fc_objects);
	}

	/* 在这里我们真正删除它们。 */
	performMultipleDeletions(fc_objects, fc_stmt->behavior, 0);

	free_object_addresses(fc_objects);
}

/*
 * owningrel_does_not_exist_skipping
 *		RemoveObjects 的子例程
 *
 * 在确定规则或触发器的规范返回指定对象不存在后，测试其拥有关系及其模式是否存在；
 * 如果存在，则返回 false --- 实际上是触发器或规则本身丢失。 如果拥有关系或其模式不存在，
 * 填充错误消息格式字符串和名称，并返回 true。
 */
static bool fc_owningrel_does_not_exist_skipping(List *fc_object, const char **fc_msg, char **fc_name)
{
	List	   *fc_parent_object;
	RangeVar   *fc_parent_rel;

	fc_parent_object = list_truncate(list_copy(fc_object),
								  list_length(fc_object) - 1);

	if (fc_schema_does_not_exist_skipping(fc_parent_object, fc_msg, fc_name))
		return true;

	fc_parent_rel = makeRangeVarFromNameList(fc_parent_object);

	if (!OidIsValid(RangeVarGetRelid(fc_parent_rel, NoLock, true)))
	{
		*fc_msg = gettext_noop("relation \"%s\" does not exist, skipping");
		*fc_name = NameListToString(fc_parent_object);

		return true;
	}

	return false;
}

/*
 * schema_does_not_exist_skipping
 *		RemoveObjects 的子例程
 *
 * 在确定模式限定对象的规范引用不存在的对象后，测试指定模式是否存在。
 * 如果没有指定模式，或如果模式存在，则返回 false -- 实际上是对象本身丢失。 
 * 如果指定的模式不存在，填充错误消息格式字符串和指定的模式名称，并返回 true。
 */
static bool fc_schema_does_not_exist_skipping(List *fc_object, const char **fc_msg, char **fc_name)
{
	RangeVar   *fc_rel;

	fc_rel = makeRangeVarFromNameList(fc_object);

	if (fc_rel->schemaname != NULL &&
		!OidIsValid(LookupNamespaceNoError(fc_rel->schemaname)))
	{
		*fc_msg = gettext_noop("schema \"%s\" does not exist, skipping");
		*fc_name = fc_rel->schemaname;

		return true;
	}

	return false;
}

/*
 * type_in_list_does_not_exist_skipping
 *		RemoveObjects 的子例程
 *
 * 在确定函数、类型转换、聚合或运算符的规范返回指定对象不存在后，
 * 测试相关数据类型及其模式是否存在；如果存在，则返回 false --- 原始对象本身丢失。 
 * 如果数据类型或模式不存在，填充错误消息格式字符串和缺失的名称，并返回 true。
 *
 * 第一个参数是 TypeNames 的列表。
 */
static bool fc_type_in_list_does_not_exist_skipping(List *fc_typenames, const char **fc_msg,
									 char **fc_name)
{
	ListCell   *fc_l;

	foreach(fc_l, fc_typenames)
	{
		TypeName   *fc_typeName = lfirst_node(TypeName, fc_l);

		if (fc_typeName != NULL)
		{
			if (!OidIsValid(LookupTypeNameOid(NULL, fc_typeName, true)))
			{
				/* 类型不存在，尝试找出原因 */
				if (fc_schema_does_not_exist_skipping(fc_typeName->names, fc_msg, fc_name))
					return true;

				*fc_msg = gettext_noop("type \"%s\" does not exist, skipping");
				*fc_name = TypeNameToString(fc_typeName);

				return true;
			}
		}
	}

	return false;
}

/*
 * does_not_exist_skipping
 *		RemoveObjects 的子例程
 *
 * 生成一个 NOTICE，说明未找到命名对象，正在跳过它。 
 * 这仅在使用 "IF EXISTS" 时相关；否则，
 * RemoveObjects 中的 get_object_address() 将抛出 ERROR。
 */
static void fc_does_not_exist_skipping(ObjectType fc_objtype, Node *fc_object)
{
	const char *fc_msg = NULL;
	char	   *fc_name = NULL;
	char	   *fc_args = NULL;

	switch (fc_objtype)
	{
		case OBJECT_ACCESS_METHOD:
			fc_msg = gettext_noop("access method \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		case OBJECT_TYPE:
		case OBJECT_DOMAIN:
			{
				TypeName   *fc_typ = castNode(TypeName, fc_object);

				if (!fc_schema_does_not_exist_skipping(fc_typ->names, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("type \"%s\" does not exist, skipping");
					fc_name = TypeNameToString(fc_typ);
				}
			}
			break;
		case OBJECT_COLLATION:
			if (!fc_schema_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("collation \"%s\" does not exist, skipping");
				fc_name = NameListToString(castNode(List, fc_object));
			}
			break;
		case OBJECT_CONVERSION:
			if (!fc_schema_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("conversion \"%s\" does not exist, skipping");
				fc_name = NameListToString(castNode(List, fc_object));
			}
			break;
		case OBJECT_SCHEMA:
			fc_msg = gettext_noop("schema \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		case OBJECT_STATISTIC_EXT:
			if (!fc_schema_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("statistics object \"%s\" does not exist, skipping");
				fc_name = NameListToString(castNode(List, fc_object));
			}
			break;
		case OBJECT_TSPARSER:
			if (!fc_schema_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("text search parser \"%s\" does not exist, skipping");
				fc_name = NameListToString(castNode(List, fc_object));
			}
			break;
		case OBJECT_TSDICTIONARY:
			if (!fc_schema_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("text search dictionary \"%s\" does not exist, skipping");
				fc_name = NameListToString(castNode(List, fc_object));
			}
			break;
		case OBJECT_TSTEMPLATE:
			if (!fc_schema_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("text search template \"%s\" does not exist, skipping");
				fc_name = NameListToString(castNode(List, fc_object));
			}
			break;
		case OBJECT_TSCONFIGURATION:
			if (!fc_schema_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("text search configuration \"%s\" does not exist, skipping");
				fc_name = NameListToString(castNode(List, fc_object));
			}
			break;
		case OBJECT_EXTENSION:
			fc_msg = gettext_noop("extension \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		case OBJECT_FUNCTION:
			{
				ObjectWithArgs *fc_owa = castNode(ObjectWithArgs, fc_object);

				if (!fc_schema_does_not_exist_skipping(fc_owa->objname, &fc_msg, &fc_name) &&
					!fc_type_in_list_does_not_exist_skipping(fc_owa->objargs, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("function %s(%s) does not exist, skipping");
					fc_name = NameListToString(fc_owa->objname);
					fc_args = TypeNameListToString(fc_owa->objargs);
				}
				break;
			}
		case OBJECT_PROCEDURE:
			{
				ObjectWithArgs *fc_owa = castNode(ObjectWithArgs, fc_object);

				if (!fc_schema_does_not_exist_skipping(fc_owa->objname, &fc_msg, &fc_name) &&
					!fc_type_in_list_does_not_exist_skipping(fc_owa->objargs, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("procedure %s(%s) does not exist, skipping");
					fc_name = NameListToString(fc_owa->objname);
					fc_args = TypeNameListToString(fc_owa->objargs);
				}
				break;
			}
		case OBJECT_ROUTINE:
			{
				ObjectWithArgs *fc_owa = castNode(ObjectWithArgs, fc_object);

				if (!fc_schema_does_not_exist_skipping(fc_owa->objname, &fc_msg, &fc_name) &&
					!fc_type_in_list_does_not_exist_skipping(fc_owa->objargs, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("routine %s(%s) does not exist, skipping");
					fc_name = NameListToString(fc_owa->objname);
					fc_args = TypeNameListToString(fc_owa->objargs);
				}
				break;
			}
		case OBJECT_AGGREGATE:
			{
				ObjectWithArgs *fc_owa = castNode(ObjectWithArgs, fc_object);

				if (!fc_schema_does_not_exist_skipping(fc_owa->objname, &fc_msg, &fc_name) &&
					!fc_type_in_list_does_not_exist_skipping(fc_owa->objargs, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("aggregate %s(%s) does not exist, skipping");
					fc_name = NameListToString(fc_owa->objname);
					fc_args = TypeNameListToString(fc_owa->objargs);
				}
				break;
			}
		case OBJECT_OPERATOR:
			{
				ObjectWithArgs *fc_owa = castNode(ObjectWithArgs, fc_object);

				if (!fc_schema_does_not_exist_skipping(fc_owa->objname, &fc_msg, &fc_name) &&
					!fc_type_in_list_does_not_exist_skipping(fc_owa->objargs, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("operator %s does not exist, skipping");
					fc_name = NameListToString(fc_owa->objname);
				}
				break;
			}
		case OBJECT_LANGUAGE:
			fc_msg = gettext_noop("language \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		case OBJECT_CAST:
			{
				if (!fc_type_in_list_does_not_exist_skipping(list_make1(linitial(castNode(List, fc_object))), &fc_msg, &fc_name) &&
					!fc_type_in_list_does_not_exist_skipping(list_make1(lsecond(castNode(List, fc_object))), &fc_msg, &fc_name))
				{
					/* XXX 是否引用 */
					fc_msg = gettext_noop("cast from type %s to type %s does not exist, skipping");
					fc_name = TypeNameToString(linitial_node(TypeName, castNode(List, fc_object)));
					fc_args = TypeNameToString(lsecond_node(TypeName, castNode(List, fc_object)));
				}
			}
			break;
		case OBJECT_TRANSFORM:
			if (!fc_type_in_list_does_not_exist_skipping(list_make1(linitial(castNode(List, fc_object))), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("transform for type %s language \"%s\" does not exist, skipping");
				fc_name = TypeNameToString(linitial_node(TypeName, castNode(List, fc_object)));
				fc_args = strVal(lsecond(castNode(List, fc_object)));
			}
			break;
		case OBJECT_TRIGGER:
			if (!fc_owningrel_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("trigger \"%s\" for relation \"%s\" does not exist, skipping");
				fc_name = strVal(llast(castNode(List, fc_object)));
				fc_args = NameListToString(list_truncate(list_copy(castNode(List, fc_object)),
													  list_length(castNode(List, fc_object)) - 1));
			}
			break;
		case OBJECT_POLICY:
			if (!fc_owningrel_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("policy \"%s\" for relation \"%s\" does not exist, skipping");
				fc_name = strVal(llast(castNode(List, fc_object)));
				fc_args = NameListToString(list_truncate(list_copy(castNode(List, fc_object)),
													  list_length(castNode(List, fc_object)) - 1));
			}
			break;
		case OBJECT_EVENT_TRIGGER:
			fc_msg = gettext_noop("event trigger \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		case OBJECT_RULE:
			if (!fc_owningrel_does_not_exist_skipping(castNode(List, fc_object), &fc_msg, &fc_name))
			{
				fc_msg = gettext_noop("rule \"%s\" for relation \"%s\" does not exist, skipping");
				fc_name = strVal(llast(castNode(List, fc_object)));
				fc_args = NameListToString(list_truncate(list_copy(castNode(List, fc_object)),
													  list_length(castNode(List, fc_object)) - 1));
			}
			break;
		case OBJECT_FDW:
			fc_msg = gettext_noop("foreign-data wrapper \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		case OBJECT_FOREIGN_SERVER:
			fc_msg = gettext_noop("server \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		case OBJECT_OPCLASS:
			{
				List	   *fc_opcname = list_copy_tail(castNode(List, fc_object), 1);

				if (!fc_schema_does_not_exist_skipping(fc_opcname, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("operator class \"%s\" does not exist for access method \"%s\", skipping");
					fc_name = NameListToString(fc_opcname);
					fc_args = strVal(linitial(castNode(List, fc_object)));
				}
			}
			break;
		case OBJECT_OPFAMILY:
			{
				List	   *fc_opfname = list_copy_tail(castNode(List, fc_object), 1);

				if (!fc_schema_does_not_exist_skipping(fc_opfname, &fc_msg, &fc_name))
				{
					fc_msg = gettext_noop("operator family \"%s\" does not exist for access method \"%s\", skipping");
					fc_name = NameListToString(fc_opfname);
					fc_args = strVal(linitial(castNode(List, fc_object)));
				}
			}
			break;
		case OBJECT_PUBLICATION:
			fc_msg = gettext_noop("publication \"%s\" does not exist, skipping");
			fc_name = strVal(fc_object);
			break;
		default:
			elog(ERROR, "unrecognized object type: %d", (int) fc_objtype);
			break;
	}

	if (!fc_args)
		ereport(NOTICE, (errmsg(fc_msg, fc_name)));
	else
		ereport(NOTICE, (errmsg(fc_msg, fc_name, fc_args)));
}
